package game.creator;

import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import game.DataStore;
import game.gameobject.wall.WallTile;
import game.gameobject.world.CeilingTile;
import game.gameobject.world.FloorTile;
import java.util.ArrayList;
import java.util.List;
import jme3tools.optimize.GeometryBatchFactory;

public abstract class WorldCreator {
    public static Material floorMaterial, wallMaterial, ceilingMaterial;

    public static void initDefaultMaterials () {
	floorMaterial = new Material(DataStore.assetManager, "Common/MatDefs/Light/Lighting.j3md");
	floorMaterial.setBoolean("UseMaterialColors",true);
	floorMaterial.setColor("Diffuse",  ColorRGBA.White);
        floorMaterial.setColor("Specular", ColorRGBA.White);

	wallMaterial = new Material(DataStore.assetManager, "Common/MatDefs/Light/Lighting.j3md");
	wallMaterial.setBoolean("UseMaterialColors",true);
	wallMaterial.setColor("Diffuse",  ColorRGBA.White);
        wallMaterial.setColor("Specular", ColorRGBA.White);

	ceilingMaterial = new Material(DataStore.assetManager, "Common/MatDefs/Light/Lighting.j3md");
	ceilingMaterial.setBoolean("UseMaterialColors",true);
	ceilingMaterial.setColor("Diffuse",  ColorRGBA.White);
        ceilingMaterial.setColor("Specular", ColorRGBA.White);
    }

    public static Node createRoom(Vector3f center, int xTiles, int yTiles, int zTiles) {
	Node room = new Node();

	Vector3f floorCenter = center.clone();
	floorCenter.setY(floorCenter.getY() - yTiles*WallTile.Y_EXTENT - FloorTile.Y_EXTENT);
	Node floor = createFloor(xTiles, zTiles);
	floor.setLocalTranslation(floorCenter);
	GeometryBatchFactory.optimize(floor);
	//for(Spatial tile : floor.getChildren())
	    //tile.addControl(new RigidBodyControl(0f));
	floor.addControl(new RigidBodyControl(0f));
	room.attachChild(floor);

	List<Node> walls = createWalls(center, xTiles, yTiles, zTiles);
	for(Node wall : walls) {
	    //optimizing like this might make something more efficient, but also has some wonky side-effects...
	    GeometryBatchFactory.optimize(wall);
	    //for(Spatial tile : wall.getChildren())
		//tile.addControl(new RigidBodyControl(0f));
	    wall.addControl(new RigidBodyControl(0f));
	    room.attachChild(wall);
	}

	Vector3f ceilingCenter = center.clone();
	ceilingCenter.setY(ceilingCenter.getY() + yTiles*WallTile.Y_EXTENT + FloorTile.Y_EXTENT);
	Node ceiling = createCeiling(xTiles, zTiles);
	ceiling.setLocalTranslation(ceilingCenter);
	GeometryBatchFactory.optimize(ceiling);
	//for(Spatial tile : ceiling.getChildren())
	    //tile.addControl(new RigidBodyControl(0f));
	ceiling.addControl(new RigidBodyControl(0f));
	room.attachChild(ceiling);

	return room;
    }

    public static Node createFloor(int xTiles, int zTiles) {
	Node floor = new Node("floor");
	
	float zStart = -zTiles*FloorTile.Z_EXTENT + FloorTile.Z_EXTENT;
	float xStart = -xTiles*FloorTile.X_EXTENT + FloorTile.X_EXTENT;

	Vector3f currentCenter = Vector3f.ZERO.clone();
	currentCenter.setZ(zStart);
	for (int z=1; z<=zTiles; z++) {
	    currentCenter.setX(xStart);
	    for (int x=1; x<=xTiles; x++) {
		FloorTile floorTile = new FloorTile(floorMaterial);
		floorTile.setLocalTranslation(currentCenter);
		floor.attachChild(floorTile);
		currentCenter.setX(currentCenter.getX() + FloorTile.X_EXTENT*2);
	    }
	    currentCenter.setZ(currentCenter.getZ() + FloorTile.Z_EXTENT*2);
	}

	return floor;
    }

    public static Node createCeiling(int xTiles, int zTiles) {
	Node ceiling = new Node("ceiling");

	float zStart = -zTiles*CeilingTile.Z_EXTENT + CeilingTile.Z_EXTENT;
	float xStart = -xTiles*CeilingTile.X_EXTENT + CeilingTile.X_EXTENT;

	Vector3f currentCenter = Vector3f.ZERO.clone();
	currentCenter.setZ(zStart);
	for (int z=1; z<=zTiles; z++) {
	    currentCenter.setX(xStart);
	    for (int x=1; x<=xTiles; x++) {
		CeilingTile ceilingTile = new CeilingTile(ceilingMaterial);
		ceilingTile.setLocalTranslation(currentCenter);
		ceiling.attachChild(ceilingTile);
		currentCenter.setX(currentCenter.getX() + CeilingTile.X_EXTENT*2);
	    }
	    currentCenter.setZ(currentCenter.getZ() + CeilingTile.Z_EXTENT*2);
	}

	return ceiling;
    }

    public static List<Node> createWalls(Vector3f roomCenter, int xTiles, int yTiles, int zTiles) {
	List<Node> walls = new ArrayList<Node>();

	Vector3f backWallCenter = roomCenter.clone();
	backWallCenter.setZ(backWallCenter.getZ() - zTiles*FloorTile.Z_EXTENT - WallTile.Z_EXTENT);
	Node backWall = createWall(xTiles, yTiles);
	backWall.setLocalTranslation(backWallCenter);
	walls.add(backWall);

	Vector3f frontWallCenter = roomCenter.clone();
	frontWallCenter.setZ(frontWallCenter.getZ() + zTiles*FloorTile.Z_EXTENT + WallTile.Z_EXTENT);
	Node frontWall = createWall(xTiles, yTiles);
	frontWall.setLocalTranslation(frontWallCenter);
	walls.add(frontWall);

	Vector3f leftWallCenter = roomCenter.clone();
	leftWallCenter.setX(leftWallCenter.getX() - xTiles*FloorTile.X_EXTENT - WallTile.Z_EXTENT);
	Node leftWall = createWall(zTiles, yTiles);
	leftWall.setLocalTranslation(leftWallCenter);
	leftWall.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.PI/2, new Vector3f(0,1,0)));
	walls.add(leftWall);

	Vector3f rightWallCenter = roomCenter.clone();
	rightWallCenter.setX(rightWallCenter.getX() + xTiles*FloorTile.X_EXTENT + WallTile.Z_EXTENT);
	Node rightWall = createWall(zTiles, yTiles);
	rightWall.setLocalTranslation(rightWallCenter);
	rightWall.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.PI/2, new Vector3f(0,1,0)));
	walls.add(rightWall);

	return walls;
    }

    /** Creates a forward-facing, x by y wall which can then be rotated to face the desired direction. */
    public static Node createWall(int xTiles, int yTiles) {
	Node wall = new Node("wall");

	float yStart = -yTiles*WallTile.Y_EXTENT + WallTile.Y_EXTENT;
	float xStart = -xTiles*WallTile.X_EXTENT + WallTile.X_EXTENT;

	Vector3f currentCenter = Vector3f.ZERO.clone();
	currentCenter.setY(yStart);
	for (int y=1; y<=yTiles; y++) {
	    currentCenter.setX(xStart);
	    for (int x=1; x<=xTiles; x++) {
		WallTile wallTile = new WallTile(wallMaterial);
		wallTile.setLocalTranslation(currentCenter);
		wall.attachChild(wallTile);
		currentCenter.setX(currentCenter.getX() + FloorTile.X_EXTENT*2);
	    }
	    currentCenter.setY(currentCenter.getY() + WallTile.Y_EXTENT*2);
	}

	return wall;
    }

    public static void setCeilingMaterial(Material ceilingMaterial) {
	WorldCreator.ceilingMaterial = ceilingMaterial;
    }

    public static void setFloorMaterial(Material floorMaterial) {
	WorldCreator.floorMaterial = floorMaterial;
    }

    public static void setWallMaterial(Material wallMaterial) {
	WorldCreator.wallMaterial = wallMaterial;
    }
}
